package trackClustering.example.spatial;

import trackClustering.example.entity.TrajectoryPoint;

import java.util.*;
import java.util.stream.Collectors;

public class TrajectoryProcessorByDbscan {
    private final double eps;
    private final int minPts;

    public TrajectoryProcessorByDbscan(double eps, int minPts) {
        this.eps = eps;
        this.minPts = minPts;
    }
    // 修改静态入口方法支持自动计算eps
    public static Map<Integer, List<TrajectoryPoint>> clusterPoints(List<TrajectoryPoint> points, double eps, int minPts) {
        if (eps < 0 ) {
            // 当eps为负数时触发自动计算
            eps = computeEps(points, minPts);
        }
        System.out.println("空间minPts: " + minPts);
        System.out.println("空间eps: " + eps);
        TrajectoryProcessorByDbscan dbscan = new TrajectoryProcessorByDbscan(eps, minPts);
        return dbscan.performClustering(points);
    }

    public Map<Integer, List<TrajectoryPoint>> performClustering(List<TrajectoryPoint> points) {
        Map<Integer, List<TrajectoryPoint>> clusters = new HashMap<>();
        int clusterId = 0;
        Set<TrajectoryPoint> visited = new HashSet<>();

        for (TrajectoryPoint point : points) {
            if (!visited.contains(point)) {
                visited.add(point);
                List<TrajectoryPoint> neighbors = regionQuery(point, points);

                if (neighbors.size() < minPts) {
                    // Mark as noise
                } else {
                    clusterId++;
                    expandCluster(clusters, clusterId, point, neighbors, points, visited);
                }
            }
        }
        return clusters;
    }

    private void expandCluster(Map<Integer, List<TrajectoryPoint>> clusters, int clusterId, TrajectoryPoint point,
                               List<TrajectoryPoint> neighbors, List<TrajectoryPoint> points, Set<TrajectoryPoint> visited) {
        // 将当前点加入簇中
        clusters.computeIfAbsent(clusterId, k -> new ArrayList<>()).add(point);

        // 使用一个集合来跟踪需要进一步检查的邻居
        Set<TrajectoryPoint> toBeChecked = new HashSet<>(neighbors);

        while (!toBeChecked.isEmpty()) {
            Iterator<TrajectoryPoint> iterator = toBeChecked.iterator();
            TrajectoryPoint currentPoint = iterator.next();
            iterator.remove(); // 从toBeChecked中移除当前点

            if (!visited.contains(currentPoint)) {
                visited.add(currentPoint);
                List<TrajectoryPoint> currentNeighbors = regionQuery(currentPoint, points);
                if (currentNeighbors.size() >= minPts) {
                    // 添加新的邻居到toBeChecked
                    toBeChecked.addAll(currentNeighbors);
                }
            }

            // 如果该点还没有被分配到任何簇，则将其添加到当前簇
            if (clusters.values().stream().noneMatch(list -> list.contains(currentPoint))) {
                clusters.get(clusterId).add(currentPoint);
            }
        }
    }

    private List<TrajectoryPoint> regionQuery(TrajectoryPoint point, List<TrajectoryPoint> points) {
        return points.stream()
                .filter(other -> calculateDistance(point, other) <= eps)
                .collect(Collectors.toList());
    }

//    private double calculateDistance(TrajectoryPoint p1, TrajectoryPoint p2) {
//        double lat1 = Math.toRadians(p1.getLatitude());
//        double lon1 = Math.toRadians(p1.getLongitude());
//        double lat2 = Math.toRadians(p2.getLatitude());
//        double lon2 = Math.toRadians(p2.getLongitude());
//
//        double dLat = lat2 - lat1;
//        double dLon = lon2 - lon1;
//        double a = Math.pow(Math.sin(dLat / 2), 2) +
//                Math.cos(lat1) * Math.cos(lat2) *
//                        Math.pow(Math.sin(dLon / 2), 2);
//        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
//        double distanceOnEarthSurface = 6371 * c; // 地球半径约为6371公里
//
//        double heightDifference = (p1.getHeight() - p2.getHeight()) / 1000.0;
//        double calculateDistance= Math.sqrt(Math.pow(distanceOnEarthSurface, 2) + Math.pow(heightDifference, 2));
//        return calculateDistance;
//    }

    public static double calculateDistance(TrajectoryPoint p1, TrajectoryPoint p2) {
        double deltaX = p2.getEasting() - p1.getEasting();
        double deltaY = p2.getNorthing() - p1.getNorthing();
        double heightDifference = (p1.getHeight() - p2.getHeight());
        return Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2) + Math.pow(heightDifference, 2));
    }
    // 新增eps自动计算方法
    public static double computeQuantile(List<Double> distances, double quantile) {
        int index = (int) Math.ceil(distances.size()* quantile);
        Collections.sort(distances);
        return distances.get(index - 1);
    }

    private static double computeEps(List<TrajectoryPoint> points, int minPts) {
        int k = minPts-1 ;
        List<Double> kDistances = new ArrayList<>();

        for (TrajectoryPoint point : points) {
            List<Double> distances = new ArrayList<>();
            for (TrajectoryPoint other : points) {
                if (point != other) {
                    distances.add(calculateDistance(point, other));
                }
            }
            if (distances.size() >= k) {
                Collections.sort(distances);
                kDistances.add(distances.get(k - 1));
                // 取第k近邻距离
            }
        }
        return computeQuantile(kDistances,1);
    }








}
